package com.hwpt.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.hwpt.dto.user.SendCodeDTO;
import com.hwpt.dto.user.VerifyCodeDTO;
import com.hwpt.exception.AuthException;
import com.hwpt.service.UserInfoService;
import com.hwpt.service.VerificationCodeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.HashMap;
import java.util.concurrent.TimeUnit;

import static com.hwpt.constant.ExceptionMessageConstant.*;
import static com.hwpt.constant.LimitConstant.*;
import static com.hwpt.constant.RedisKeyConstant.*;

@Service
@RequiredArgsConstructor
@Slf4j
public class VerificationCodeServiceImpl implements VerificationCodeService {

    private final RedisTemplate<String, Object> redisTemplate;
    private final UserInfoService userInfoService;

    // 发送验证码
    @Override
    public void sendVerificationCode(SendCodeDTO sendCodeDTO) {
        log.info("开始处理验证码发送请求，手机号：{}", sendCodeDTO.getPhone());
        String phone = sendCodeDTO.getPhone();
        String type = sendCodeDTO.getType();
        log.info("验证码发送类型：{}", type);

        if (type.equals("register")) {
            userInfoService.checkPhoneNotRegistered(phone);
        } else if (type.equals("login") || type.equals("ResetPassword")) {
            userInfoService.checkPhoneRegistered(phone);
        }


        //限流策略
        checkSmsLimit(phone);

        String verifyCode = RandomUtil.randomNumbers(6);
        log.info("生成验证码成功，手机号：{}，验证码：{}", phone, verifyCode);

        cacheVerificationCode(phone, verifyCode);
        
        //TODO调用第三方短信服务发送验证码
        log.info("验证码发送流程完成，手机号：{}", phone);
    }

    //校验验证码,如果正确就把状态改为used
    @Override
    public void verifyCode(VerifyCodeDTO verifyCodeDTO) {
        String phone = verifyCodeDTO.getPhone();
        String inputCode = verifyCodeDTO.getVerifyCode();
        log.info("开始处理验证码验证请求，手机号：{}，输入验证码：{}", phone, inputCode);

        String cacheCode = getCachedVerificationCode(phone);
        if (ObjectUtils.isEmpty(cacheCode)) {
            log.warn("验证码验证失败：已过期，手机号：{}", phone);
            throw new AuthException(VERIFY_CODE_EXPIRED);
        }

        if (!cacheCode.equals(inputCode)) {
            log.warn("验证码验证失败：输入错误，手机号：{}", phone);
            throw new AuthException(VERIFY_CODE_ERROR);
        }

        markVerificationCodeAsUsed(phone);
        log.info("验证码验证成功，手机号：{}", phone);
    }

    // 校验验证码是否已被验证
    @Override
    public boolean isVerificationCodeVerified(String phone) {
        String verifyCodeKey = VERIFY_CODE_KEY_PREFIX + phone;
        String status = (String) redisTemplate.opsForHash().get(verifyCodeKey, "status");
        return "used".equals(status);
    }

    // 缓存验证码
    private void cacheVerificationCode(String phone, String verifyCode) {
        String verifyCodeKey = VERIFY_CODE_KEY_PREFIX + phone;
        HashMap<String, Object> codeCache = new HashMap<>(2);
        codeCache.put("verifyCode", verifyCode);
        codeCache.put("status", "unused");
        redisTemplate.opsForHash().putAll(verifyCodeKey, codeCache);
        redisTemplate.expire(verifyCodeKey, 5, TimeUnit.MINUTES);
    }

    // 获取缓存的验证码
    private String getCachedVerificationCode(String phone) {
        String verifyCodeKey = VERIFY_CODE_KEY_PREFIX + phone;
        return (String) redisTemplate.opsForHash().get(verifyCodeKey, "verifyCode");
    }

    // 标记验证码为已使用
    private void markVerificationCodeAsUsed(String phone) {
        String verifyCodeKey = VERIFY_CODE_KEY_PREFIX + phone;
        redisTemplate.opsForHash().put(verifyCodeKey, "status", "used");
    }


    // 校验短信发送限流
    private void checkSmsLimit(String phone) {
        // 分钟级限流
        String minuteKey = LIMIT_SMS_MINUTE_KEY_PREFIX + phone;
        Long minuteCount = redisTemplate.opsForValue().increment(minuteKey, 1);
        if (minuteCount != null && minuteCount == 1) {
            redisTemplate.expire(minuteKey, LIMIT_SMS_MINUTE_EXPIRE_MINUTES, TimeUnit.MINUTES);
        }
        if (minuteCount != null && minuteCount > LIMIT_SMS_MINUTE_COUNT) {
            log.warn("短信限流：分钟级超限，手机号：{}，当前次数：{}", phone, minuteCount);
            throw new AuthException(SMS_LIMIT_MINUTE);
        }

        // 天级限流
        String dayKey = LIMIT_SMS_DAY_KEY_PREFIX + phone;
        Long dayCount = redisTemplate.opsForValue().increment(dayKey, 1);
        if (dayCount != null && dayCount == 1) {
            redisTemplate.expire(dayKey, LIMIT_SMS_DAY_EXPIRE_SECONDS, TimeUnit.SECONDS);
        }
        if (dayCount != null && dayCount > LIMIT_SMS_DAY_COUNT) {
            log.warn("短信限流：天级超限，手机号：{}，当前次数：{}", phone, dayCount);
            throw new AuthException(SMS_LIMIT_DAY);
        }
    }
}